/* Copyright (c) 2004-2024 Peigen.info. All rights reserved. */

package info.peigen.hotpot.business.account.core.repository.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import info.peigen.hotpot.business.account.core.domain.account.Account;
import info.peigen.hotpot.business.account.core.domain.account.AccountFreezeLog;
import info.peigen.hotpot.business.account.core.domain.account.AccountLog;
import info.peigen.hotpot.business.account.core.domain.account.AccountTransLog;
import info.peigen.hotpot.business.account.core.domain.account.action.AccountConvertor;
import info.peigen.hotpot.business.account.core.repository.entity.AccountEntity;
import info.peigen.hotpot.business.account.core.repository.entity.AccountFreezeLogEntity;
import info.peigen.hotpot.business.account.core.repository.entity.AccountLogEntity;
import info.peigen.hotpot.business.account.core.repository.entity.AccountTransLogEntity;
import info.peigen.hotpot.business.account.core.repository.mapper.AccountMapper;
import info.peigen.hotpot.business.account.core.util.CreateAccountUtil;
import info.peigen.hotpot.business.account.facade.enums.AccountStatus;
import info.peigen.hotpot.business.router.facade.enums.ChannelCode;
import info.peigen.hotpot.common.core.enums.Currency;
import info.peigen.hotpot.common.core.lang.money.Money;
import info.peigen.hotpot.common.core.result.ResultCode;
import info.peigen.hotpot.common.service.base.exception.BusinessException;
import info.peigen.hotpot.component.data.repository.beetlsql.service.AbstractBeetlSqlRepository;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.FieldDefaults;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;

import java.util.List;
import java.util.stream.Collectors;

import static info.peigen.hotpot.business.account.facade.enums.AccountType.Provisions;

/**
 * <b>(AccountRepository)</b>
 *
 * @author Peigen
 * @version 1.0.0
 * @since 2021/8/5
 */
@EqualsAndHashCode(callSuper = true)
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Component
public class AccountRepository extends AbstractBeetlSqlRepository<AccountMapper, AccountEntity> {
    @Inject
    AccountLogRepository       accountLogRepository;
    @Inject
    AccountTransLogRepository  accountTransLogRepository;
    @Inject
    AccountFreezeLogRepository accountFreezeLogRepository;

    public void storeCreate(Account domain) {
        AccountEntity entity = AccountConvertor.convertAccount(domain);
        save(entity);
    }

    public void store(Account domain) {
        AccountEntity entity = AccountConvertor.convertAccount(domain);
        saveOrUpdate(entity);
    }

    public void store(AccountLog domain) {
        AccountLogEntity entity = AccountConvertor.convertAccountLog(domain);
        accountLogRepository.saveOrUpdate(entity);
    }

    public void store(AccountTransLog domain) {
        AccountTransLogEntity entity = AccountConvertor.convertAccountTransLog(domain);
        accountTransLogRepository.saveOrUpdate(entity);
    }

    public void store(AccountFreezeLog domain) {
        AccountFreezeLogEntity entity = AccountConvertor.convertAccountFreezeLog(domain);
        accountFreezeLogRepository.saveOrUpdate(entity);
    }

    public void batchStoreAccount(List<Account> accounts) {
        if (CollectionUtil.isNotEmpty(accounts)) {
            List<AccountEntity> entities = accounts.stream().filter(account -> ObjectUtil.isNotNull(account)).distinct().map(AccountConvertor::convertAccount).collect(Collectors.toList());
            saveOrUpdateBatch(entities);
        }
    }

    public void batchStoreAccountLog(List<AccountLog> accountLogs) {
        List<AccountLogEntity> entities = accountLogs.stream().filter(accountLog -> ObjectUtil.isNotNull(accountLog)).distinct().map(AccountConvertor::convertAccountLog).collect(Collectors.toList());
        accountLogRepository.saveOrUpdateBatch(entities);
    }

    public void batchStoreAccountTransLog(List<AccountTransLog> accountLogs) {
        List<AccountTransLogEntity> entities = accountLogs.stream().filter(accountTransLog -> ObjectUtil.isNotNull(accountTransLog)).distinct().map(AccountConvertor::convertAccountTransLog).collect(Collectors.toList());
        accountTransLogRepository.saveOrUpdateBatch(entities);
    }

    public Account active(String accountId) {
        AccountEntity entity = getMapper().findByAccountId(accountId);

        Assert.notNull(entity, () -> new BusinessException(ResultCode.FAILURE, "激活失败!accountId=" + accountId));

        return AccountConvertor.convertAccount(entity);
    }

    public Account activeByCustomerId(String customerId) {
        AccountEntity entity = getMapper().findByCustomerId(customerId);

        Assert.notNull(entity, () -> new BusinessException(ResultCode.FAILURE, "激活失败!customerId=" + customerId));

        return AccountConvertor.convertAccount(entity);
    }

    public List<Account> activeBatch(List<String> accountIds) {
        List<AccountEntity> entities = getMapper().createLambdaQuery().andIn(AccountEntity::getAccountId, accountIds).select();
        return entities.stream().map(AccountConvertor::convertAccount).collect(Collectors.toList());
    }

    public Account lock(String accountId) {
        Assert.isTrue(StrUtil.isNotEmpty(accountId), () -> new BusinessException(ResultCode.FAILURE, "加锁时[accountId]不得为空!"));
        return AccountConvertor.convertAccount(getMapper().lockByAccountId(accountId));
    }

    public Account lockProvisions(ChannelCode channelCode) {
        Assert.notNull((channelCode), () -> new BusinessException(ResultCode.FAILURE, "加锁时[channelCode]不得为空!"));
        AccountEntity entity = getMapper().lockProvisionsAccountByCode(channelCode);

        // 如果没有备付金账户则新增一个
        if (ObjectUtil.isNull(entity)) {
            entity = getOrCreateProvisionsAccountEntity(channelCode);
            return lock(entity.getAccountId());
        }
        return AccountConvertor.convertAccount(entity);
    }

    public AccountEntity getOrCreateProvisionsAccountEntity(ChannelCode code) {
        AccountEntity entity = getMapper().findProvisionsAccountByCode(code);

        if (ObjectUtil.isNull(entity)) {
            String        accountId     = CreateAccountUtil.createProvisionsAccountId();
            AccountEntity accountEntity = AccountEntity.builder().accountType(Provisions).customerId(accountId).accountId(accountId).channelCode(code).currency(Currency.CNY).balance(Money.zero()).status(AccountStatus.Normal).aliasName("[" + code.message() + "]备付金账户").build();
            saveOrUpdate(accountEntity);
            return accountEntity;
        }

        return entity;
    }

    public void batchStoreAccountFreezeLog(List<AccountFreezeLog> accountFreezeLogs) {
        List<AccountFreezeLogEntity> entities = accountFreezeLogs.stream().map(accountFreezeLog -> AccountConvertor.convertAccountFreezeLogEntity(accountFreezeLog)).collect(Collectors.toList());
        accountFreezeLogRepository.saveOrUpdateBatch(entities);
    }
}